package ncrnadb.ncinetview.internal.request;

import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.TreeMap;
import java.util.TreeSet;

import ncrnadb.ncinetview.internal.cy.CyUtils;
import ncrnadb.ncinetview.internal.owrapper.EntityType;
import ncrnadb.ncinetview.internal.owrapper.ORDB;
import ncrnadb.ncinetview.internal.owrapper.RID;
import ncrnadb.ncinetview.internal.owrapper.RelationFields;

import org.cytoscape.model.CyColumn;
import org.cytoscape.model.CyEdge;
import org.cytoscape.model.CyNetwork;
import org.cytoscape.model.CyNode;
import org.cytoscape.model.CyRow;

import com.tinkerpop.blueprints.Direction;
import com.tinkerpop.blueprints.Edge;
import com.tinkerpop.blueprints.Vertex;


public class ListRequest {

	
	
	public CyNetwork network = null;
	
	public CyColumn sourceTypeCol = null;	
	public CyColumn sourceNameCol = null;
	
	public boolean expand = false;
	public boolean expandWithNcrna = false;
	public boolean expandWithRna = false;
	public boolean expandWithGene = false;
	public boolean expandWithDisease = false;
	public boolean expandWithOthers = false;
	
	
	public ListRequest(){
	}
	
	
	
	public Collection<ListRequestResult> search(Collection<Collection<String>> aliases){
		Collection<ListRequestResult> ress = new LinkedList<ListRequestResult>();
		
		for(Collection<String> as : aliases){
			ress.add(searchOne(as));
		}
		
		return ress;
	}
	public ListRequestResult searchOne(Collection<String> aliases){
		
		Set<String> fAliases = new TreeSet<String>();
		Set<String> fRIDs = new TreeSet<String>();
		
		ORDB ordb = new ORDB();
		ordb.open();
		for(String alias : aliases){
			RID rid = RID.get(alias);
			if(rid != null){
				Collection<Vertex> entities = ordb.getEntities(rid);
				for(Vertex v : entities){
					fRIDs.add(v.getId().toString());
				}
				Collection<RID> rids = ordb.getRIDs(entities);
				for(RID r : rids){
					fAliases.add(r.toString());
				}
			}
		}		
		ordb.close();
		
		ListRequestResult res = new ListRequestResult();
		res.inputAliases = aliases;
		res.allAliases = fAliases;
		res.entitiesRIDs = fRIDs;
		
		return res;
	}
	
	
	public void addToNetwork(Collection<ListRequestResult> lrrs){
		//create cynode, one per result
		//add edges
		if(lrrs != null && lrrs.size() > 0 && network!=null && sourceTypeCol!=null && sourceNameCol!=null){
			
			/*===================================================================================================================*/
			//add columns to cynetwork, if they dot not exist
			CyUtils.addORIDColumns(network);
			
			
			/*===================================================================================================================*/
			//open db connection
			
			ORDB ordb = new ORDB();
			ordb.open();
			
			
			
			/*===================================================================================================================*/
			System.out.println("retrieving nodes...");
			
			//map cynode -> bioentities
			Map<Long, Set<Vertex>> cy2ov = new TreeMap<Long, Set<Vertex>>();
			//map bioentity -> cynodes
			Map<Vertex, Set<Long>> ov2cy = new TreeMap<Vertex, Set<Long>>();
			
			
			
			
			
			for(ListRequestResult lrr : lrrs){
				Collection<RID> rids = RID.getRIDs(lrr.entitiesRIDs, null);
				Collection<Vertex> ents = ordb.getEntities(rids);
				
				if(ents != null && ents.size() > 0){
					CyNode cnode = CyUtils.addEntityToNetwork(lrr.allAliases, ents, ordb, network, this.sourceTypeCol, this.sourceNameCol);
					
					Long rowId = cnode.getSUID();
					
					//map cy to OR and viceversa
					for(Vertex v : ents){
						Set<Long> nodes = ov2cy.get(v);
						if(nodes == null){
							nodes = new TreeSet<Long>();
							ov2cy.put(v, nodes);
						}
						
						Set<Vertex> verts = cy2ov.get(rowId);
						if(verts == null){
							verts = new TreeSet<Vertex>();
							cy2ov.put(rowId, verts);
						}
						
						nodes.add(rowId);
						verts.add(v);
					}
				}
			}
			
			
			
			
			
			/*===================================================================================================================*/
			//expand network with db neighbors
			System.out.println("retrieving neighbours...");
			
			if(expand){
				//types of neighbours to be included
				Set<Integer> neighTypes = new TreeSet<Integer>();
				if(expand){
					if(expandWithNcrna) neighTypes.add(ordb.getOEntityType(EntityType.NCRNA));
					if(expandWithRna) neighTypes.add(ordb.getOEntityType(EntityType.RNA));
					if(expandWithGene) neighTypes.add(ordb.getOEntityType(EntityType.GENE));
					if(expandWithDisease) neighTypes.add(ordb.getOEntityType(EntityType.DISEASE));
					if(expandWithOthers) neighTypes.add(ordb.getOEntityType(EntityType.OTHERS));
				}
				
				//we will add new cynodes and entities to maps, later
				Map<Vertex,Set<Long>> toAdd = new TreeMap<Vertex, Set<Long>>();
				
				for(Vertex v : ov2cy.keySet()){
					
					Collection<Vertex> neighs = ordb.getEntityNeighborsByClusters(v, neighTypes);
					
					for(Vertex n : neighs){
						if(!ov2cy.containsKey(n) && ! toAdd.containsKey(n)){
							//new entity to be add to cyto as new cynode
							CyNode cnode = CyUtils.addEntityToNetwork(ordb, n, network, sourceTypeCol, sourceNameCol);
						
							//add it to maps
							Set<Long> nodes = toAdd.get(n);
							if(nodes == null){
								nodes = new TreeSet<Long>();
								toAdd.put(n, nodes);
							}
							Set<Vertex> verts = cy2ov.get(cnode.getSUID());
							if(verts == null){
								verts = new TreeSet<Vertex>();
								cy2ov.put(cnode.getSUID(), verts);
							}
							nodes.add(cnode.getSUID());
							verts.add(v);
						}
					}
				}
			
				ov2cy.putAll(toAdd);
				toAdd = null;
			}
			
			
			/*===================================================================================================================*/
			//expand network with db edges
			System.out.println("retrieving edges...");

			for(Edge ed : ordb.getRelationsAmong(ov2cy.keySet())){
				RelationFields rfs = ordb.getFields(ed);
				
				Collection<CyRow> matchingRows = network.getDefaultEdgeTable().getMatchingRows(CyUtils.ORIDEdgeColumnName, ed.getId().toString());
				if(matchingRows.isEmpty()){
					
					Collection<Long> sources = ov2cy.get(ed.getVertex(Direction.OUT));
					Collection<Long> targets = ov2cy.get(ed.getVertex(Direction.IN));
					
					for(Long s : sources){
						for(Long t : targets){
						
							CyEdge ced = network.addEdge(network.getNode(s), network.getNode(t), false);
							CyRow cedRow = network.getDefaultEdgeTable().getRow(ced.getSUID());
							cedRow.set("level", rfs.level);
							cedRow.set("pubmed", rfs.pubmedID);
							cedRow.set("support", rfs.supportSentence);
							cedRow.set("source", rfs.source.compact());
							cedRow.set(CyUtils.ORIDEdgeColumnName, ed.getId().toString());
						}
					}
				}
			}
			
			
			ordb.close();
		}
	}
	
}
